
Imports System
Imports System.Data.SqlClient
Imports _2bMatesSite.Common

Namespace _2bMatesSite.BO
	Public Class GroupMembershipException
		Inherits Exception
		Public Sub New(ByVal msg As String)
			MyBase.New(msg)
		End Sub
	End Class

	''' <summary>
	''' Represents a user's relation with a group.
	''' </summary>
	Public Class GroupMember
		Protected ID As Integer
		Protected groupID As Integer
		Protected userID As Integer
		Protected user As User

		Protected acceptModeratorBulletin As Boolean
		Protected acceptMemberBulletin As Boolean

		Protected attempts As Integer

		Protected status As JoinStatus
		' 0-initial; 1-accepted; 2-rejected
		' -1 - not yet initiated
		Protected moderatorMode As Boolean
		Protected isNewMembership As Boolean

		Private statusChanged As Boolean = False

		' Group user statuses
		Public Enum JoinStatus
			jsNonExisting = -1
			jsWaitingApproval = 0
			jsAccepted = 1
			jsRejected = 2
		End Enum

		Public Sub New(ByVal groupID As Integer, ByVal userID As Integer)
			Me.groupID = groupID
			Me.userID = userID

			Me.acceptModeratorBulletin = True
			Me.acceptMemberBulletin = True

			Me.attempts = 0
			Me.status = JoinStatus.jsNonExisting

			Me.moderatorMode = False
			Me.isNewMembership = True
		End Sub

		Public Sub New(ByVal dr As SqlDataReader)
			Init(dr)
		End Sub

		Public Overloads Overrides Function GetHashCode() As Integer
			Return userID
		End Function

		Public ReadOnly Property GroupID() As Integer
			Get
				Return groupID
			End Get
		End Property

		Public ReadOnly Property UserID() As Integer
			Get
				Return userID
			End Get
		End Property

		' Returns true if the membership object is in moderation mode (set to enable moderators manipulating it)
		Public Property ModeratorMode() As Boolean
			Get
				Return moderatorMode
			End Get

			Set
				moderatorMode = value
			End Set
		End Property

		Public ReadOnly Property IsNewMembership() As Boolean
			Get
				Return isNewMembership
			End Get
		End Property

		Public ReadOnly Property User() As User
			Get
				If user = Nothing Then
					user = Users.GetUserByID(userID)
				End If
				Return user
			End Get
		End Property

		Public ReadOnly Property Attempts() As Integer
			Get
				Return attempts
			End Get
		End Property

		Public ReadOnly Property Status() As JoinStatus
			Get
				Return status
			End Get
		End Property

		Public Property AcceptModeratorBulletin() As Boolean
			Get
				Return acceptModeratorBulletin
			End Get

			Set
				acceptModeratorBulletin = value
			End Set
		End Property

		Public Property AcceptMemberBulletin() As Boolean
			Get
				Return acceptMemberBulletin
			End Get

			Set
				acceptMemberBulletin = value
			End Set
		End Property

		Public Sub Save()
			If status = JoinStatus.jsNonExisting Then
				DoDelete()
			Else
				DoSave()
			End If
		End Sub

		Public Sub Delete()
			' Attempt to delete the membership, but leave it to the
			' state machine first to avaluate
			DecrementStatus()
			Save()
		End Sub


		' The following methods
		' implement the membership state machine
		' - public groups move the status to accepted at all times
		Public Function IncrementStatus(ByVal isGroupPublic As Boolean) As JoinStatus
			statusChanged = True
			Select Case status
				Case JoinStatus.jsNonExisting
					' it's a fresh new invitation
					status = IIf(isGroupPublic,JoinStatus.jsAccepted,JoinStatus.jsWaitingApproval)
					' increment attempts count
					System.Math.Max(System.Threading.Interlocked.Increment(attempts),attempts - 1)
					Exit Select
				Case JoinStatus.jsWaitingApproval

					If ModeratorMode OrElse isGroupPublic Then
						status = JoinStatus.jsAccepted
					End If
					Exit Select
				Case JoinStatus.jsAccepted

					Throw New FriendshipException("Membership was already accepted.")
				Case JoinStatus.jsRejected

					' another membership request attempt
					If moderatorMode OrElse isGroupPublic Then
						status = JoinStatus.jsAccepted
					Else
						If attempts >= Constants.MEMBERSHIP_MAX_REQUESTS Then
							Throw New GroupMembershipException("Can not request membership more than " + Constants.MEMBERSHIP_MAX_REQUESTS.ToString() + " time(s).")
						End If

						status = JoinStatus.jsWaitingApproval
					End If

					' increment attempts count
					System.Math.Max(System.Threading.Interlocked.Increment(attempts),attempts - 1)
					Exit Select
			End Select

			Return status
		End Function

		Public Function DecrementStatus() As JoinStatus
			Select Case status
				Case JoinStatus.jsNonExisting
					Throw New GroupMembershipException("Can not reject a non-existing membership.")
				Case JoinStatus.jsWaitingApproval

					' reject the membership, moderator or not (== cancel pending membership request)
					status = JoinStatus.jsRejected
					Exit Select
				Case JoinStatus.jsAccepted

					' cancel membership
					status = JoinStatus.jsNonExisting
					Exit Select
				Case JoinStatus.jsRejected

					Throw New GroupMembershipException("Membership already rejected.")
			End Select

			Return status
		End Function



		'
		' Private and protected methods
		'

		Protected Sub Init(ByVal dr As SqlDataReader)
			Me.groupID = dr.GetInt32(dr.GetOrdinal("GroupID"))
			Me.userID = dr.GetInt32(dr.GetOrdinal("UID"))

			Me.acceptModeratorBulletin = dr.GetBoolean(dr.GetOrdinal("AcceptModeratorBulletin"))
			Me.acceptMemberBulletin = dr.GetBoolean(dr.GetOrdinal("AcceptMemberBulletin"))

			Me.attempts = dr.GetInt32(dr.GetOrdinal("Attempts"))
			Me.status = DirectCast(dr.GetInt32(dr.GetOrdinal("Status")), JoinStatus)

			Me.moderatorMode = False
			Me.isNewMembership = False
		End Sub

		Protected Sub DoSave()
			Dim conn As SqlConnection = DBManager.GetUnmanagedConnection()
			Dim tran As SqlTransaction = conn.BeginTransaction()

			Try
				Dim cmd As SqlCommand = Nothing

				If IsNewMembership Then
					cmd = New SqlCommand("INSERT INTO GroupMembers (GroupID, UID, AcceptModeratorBulletin, AcceptMemberBulletin, Attempts, Status) VALUES (@GroupID, @UID, @AcceptModeratorBulletin, @AcceptMemberBulletin, @Attempts, @Status)")
				Else
					cmd = New SqlCommand("UPDATE GroupMembers SET AcceptModeratorBulletin=@AcceptModeratorBulletin, AcceptMemberBulletin=@AcceptMemberBulletin, Attempts=@Attempts, Status=@Status WHERE GroupID=@GroupID AND UID=@UID")
				End If

				cmd.Connection = conn
				cmd.Transaction = tran

				cmd.Parameters.Add("@GroupID", groupID)
				cmd.Parameters.Add("@UID", userID)
				cmd.Parameters.Add("@AcceptModeratorBulletin", acceptModeratorBulletin)
				cmd.Parameters.Add("@AcceptMemberBulletin", acceptMemberBulletin)
				cmd.Parameters.Add("@Attempts", attempts)
				cmd.Parameters.Add("@Status", DirectCast(status, Integer))

				cmd.ExecuteNonQuery()

				tran.Commit()
			Catch e As Exception
				tran.Rollback()
				Throw e
			Finally
				conn.Close()
			End Try

			' send email notifications
			If statusChanged Then
				Select Case status
					Case JoinStatus.jsWaitingApproval
						GroupNotifications.SendMembershipRequestNotification(Me, groupID)
						Exit Select
					Case JoinStatus.jsAccepted

						'if ( !IsNewMembership )
						'	GroupNotifications.SendMembershipAcceptedNotification( this, groupID );
						'break;

						If IsNewMembership Then
							GroupNotifications.SendMembershipNotification(Me, groupID)
						Else
							GroupNotifications.SendMembershipAcceptedNotification(Me, groupID)
						End If
						Exit Select
				End Select
			End If
		End Sub

		Protected Sub DoDelete()
			Dim conn As SqlConnection = DBManager.GetUnmanagedConnection()

			Try
				Dim cmd As New SqlCommand("DELETE FROM GroupMembers WHERE GroupID=@GroupID AND UID=@UID")
				cmd.Connection = conn

				cmd.Parameters.Add("@GroupID", groupID)
				cmd.Parameters.Add("@UID", userID)

				cmd.ExecuteNonQuery()

				attempts = -1
				status = JoinStatus.jsNonExisting

				' send a notification if the moderator canceld this membership
				If moderatorMode Then
					GroupNotifications.SendMembershipModeratorCancelledNotification(User, groupID)

				End If
			Finally
				conn.Close()
			End Try
		End Sub
	End Class
End Namespace






